package cn.lingyangwl.agile.infra.job.controller;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.lingyangwl.agile.common.core.exception.TaskException;
import cn.lingyangwl.agile.file.excel.CusExcelExportUtil;
import cn.lingyangwl.agile.common.security.utils.SecurityUtils;
import cn.lingyangwl.agile.infra.job.constants.JobCons;
import cn.lingyangwl.agile.infra.job.model.entity.JobInfo;
import cn.lingyangwl.agile.infra.job.model.rqrs.JobExecuteReq;
import cn.lingyangwl.agile.infra.job.model.rqrs.JobPageReq;
import cn.lingyangwl.agile.infra.job.model.rqrs.TaskBeanResp;
import cn.lingyangwl.agile.infra.job.service.IJobInfoService;
import cn.lingyangwl.agile.infra.job.task.ITask;
import cn.lingyangwl.agile.infra.job.utils.CronUtils;
import cn.lingyangwl.agile.infra.job.utils.ScheduleUtils;
import cn.lingyangwl.agile.model.constants.CommonCons;
import cn.lingyangwl.agile.ram.ActionType;
import cn.lingyangwl.agile.ram.RamResource;
import cn.lingyangwl.framework.core.response.Resp;
import cn.lingyangwl.framework.core.utils.spring.SpringUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import org.quartz.SchedulerException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 调度任务信息操作处理
 * 
 * @author shenguangyang
 */
@Api(tags = "定时任务")
@Validated
@RestController
@RequestMapping("/job")
public class JobInfoController {
    @Resource
    private IJobInfoService jobService;

    /**
     * 分页查询定时任务列表
     */
    @RamResource(action = "monitor:job:query", name = "查询定时任务列表", type = ActionType.READ)
    @GetMapping("/page")
    public Resp<IPage<JobInfo>> pageJob(JobPageReq req) {
        IPage<JobInfo> page = jobService.pageJob(req);
        return Resp.ok(page);
    }

    @RamResource(action = "monitor:job:query", name = "查询定时任务列表", type = ActionType.READ)
    @GetMapping("/listTaskBean")
    public Resp<List<TaskBeanResp>> listTaskBean() {
        // 查询所有bean
        Map<String, ITask> beansOfType = SpringUtils.getBeansOfType(ITask.class);
        List<TaskBeanResp> data = beansOfType.keySet().stream().map(TaskBeanResp::new)
                .collect(Collectors.toList());
        return Resp.ok(data);
    }

    /**
     * 导出定时任务列表
     */
    @RamResource(action = "monitor:job:export", name = "导出定时任务", type = ActionType.READ)
    @PostMapping("/export")
    public void export(HttpServletResponse response, JobPageReq req) throws Exception {
        List<JobInfo> list = jobService.listJob(req);
        ExportParams params = new ExportParams();
        params.setType(ExcelType.HSSF);
        CusExcelExportUtil.exportExcel(response, params, "定时任务.xls", JobInfo.class, list);
    }

    /**
     * 获取定时任务详细信息
     */
    @RamResource(action = "monitor:job:get", name = "获取定时任务", type = ActionType.READ)
    @GetMapping(value = "/get")
    public Resp<JobInfo> getJob(@NotEmpty(message = "请选择任务") @RequestParam("jobId") String jobId) {
        return Resp.ok(jobService.getById(Long.valueOf(jobId)));
    }

    /**
     * 新增定时任务
     */
    @RamResource(action = "monitor:job:save", name = "保存定时任务", type = ActionType.WRITE)
    @PostMapping("/save")
    public Resp<?> save(@RequestBody JobInfo jobInfo) throws SchedulerException, TaskException {
        if (!CronUtils.isValid(jobInfo.getCronExpression())) {
            return Resp.fail("cron表达式不正确");
        } else if (StringUtils.containsIgnoreCase(jobInfo.getInvokeTarget(), JobCons.LOOKUP_RMI)) {
            return Resp.fail("新增任务'" + jobInfo.getJobName() + "'失败, 目标字符串不允许'rmi'调用");
        } else if (StringUtils.containsAnyIgnoreCase(jobInfo.getInvokeTarget(), new String[] { JobCons.LOOKUP_LDAP, JobCons.LOOKUP_LDAPS })) {
            return Resp.fail("新增任务'" + jobInfo.getJobName() + "'失败, 目标字符串不允许'ldap(s)'调用");
        } else if (StringUtils.containsAnyIgnoreCase(jobInfo.getInvokeTarget(), new String[] { CommonCons.HTTP, CommonCons.HTTPS })) {
            return Resp.fail("新增任务'" + jobInfo.getJobName() + "'失败, 目标字符串不允许'http(s)'调用");
        } else if (StringUtils.containsAnyIgnoreCase(jobInfo.getInvokeTarget(), JobCons.ERROR_STR)) {
            return Resp.fail("新增任务'" + jobInfo.getJobName() + "'失败，目标字符串存在违规");
        } else if (!ScheduleUtils.whiteList(jobInfo.getInvokeTarget())) {
            return Resp.fail("新增任务'" + jobInfo.getJobName() + "'失败，目标字符串不在白名单内");
        }
        jobInfo.setCreatedBy(SecurityUtils.getUserName());
        if (jobService.save(jobInfo) == 0) {
            throw new BizException("创建失败, 请联系管理员");
        }
        return Resp.ok();
    }

    /**
     * 修改定时任务
     */
    @RamResource(action = "monitor:job:update", name = "更新定时任务", type = ActionType.WRITE)
    @PostMapping("/edit")
    public Resp<?> edit(@RequestBody JobInfo req) throws SchedulerException, TaskException {
        if (!CronUtils.isValid(req.getCronExpression())) {
            return Resp.fail("cron表达式不正确");
        }
        if (jobService.updateJobById(req) == 0) {
            throw new BizException("更新失败");
        }
        return Resp.ok();
    }

    /**
     * 定时任务状态修改
     * @deprecated 目前前端不需要
     */
    @RamResource(action = "monitor:job:update", name = "修改定时任务状态", type = ActionType.WRITE)
    @PutMapping("/changeStatus")
    public Resp<?> changeStatus(@RequestBody JobInfo job) throws SchedulerException {
        JobInfo newJob = jobService.getById(job.getId());
        newJob.setStatus(job.getStatus());
        if (jobService.changeStatus(newJob) == 0) {
            throw new BizException("状态修改失败");
        }
        return Resp.ok();
    }

    /**
     * 恢复定时任务
     */
    @RamResource(action = "monitor:job:resume", name = "恢复定时任务", type = ActionType.WRITE)
    @GetMapping("/resume")
    public Resp<?> resume(@RequestParam(value = "id") @NotNull(message = "请选择任务") Long jobId) throws SchedulerException {
        JobInfo newJob = jobService.getById(jobId);
        newJob.setStatus(JobCons.Status.NORMAL.getValue());
        if (jobService.resumeJob(newJob) == 0) {
            throw new BizException("移除任务失败");
        }
        return Resp.ok();
    }

    /**
     * 暂停定时任务
     */
    @RamResource(action = "monitor:job:pause", name = "暂停定时任务", type = ActionType.WRITE)
    @GetMapping("/pause")
    public Resp<?> pauseJob(@RequestParam(value = "id") @NotNull(message = "请选择任务") Long jobId) throws SchedulerException {
        JobInfo newJob = jobService.getById(jobId);
        newJob.setStatus(JobCons.Status.PAUSE.getValue());
        if (jobService.pauseJob(newJob) == 0) {
            throw new BizException("暂停任务失败");
        }
        return Resp.ok();
    }

    /**
     * 定时任务立即执行一次
     */
    @RamResource(action = "monitor:job:execute", name = "立即执行一定时任务次", type = ActionType.WRITE)
    @PutMapping("/execute")
    public Resp<?> execute(@RequestBody @Validated JobExecuteReq req) throws SchedulerException {
        JobInfo job = new JobInfo();
        job.setId(req.getId());
        jobService.execute(job);
        return Resp.ok();
    }

    /**
     * 删除定时任务
     */
    @RamResource(action = "monitor:job:delete", name = "删除定时任务", type = ActionType.WRITE)
    @DeleteMapping("/delete")
    public Resp<?> delete(@RequestParam(value = "id") Long jobId) throws SchedulerException, TaskException {
        jobService.deleteByIds(Collections.singletonList(jobId));
        return Resp.ok();
    }

    @RamResource(action = "monitor:job:delete", name = "删除定时任务", type = ActionType.WRITE)
    @DeleteMapping("/batchDelete")
    public Resp<?> batchDelete(@RequestParam(value = "ids") List<Long> jobIds) throws SchedulerException, TaskException {
        jobService.deleteByIds(jobIds);
        return Resp.ok();
    }
}